package kt.com.bilin.coin.api

import kt.com.bilin.coin.bean.ResultBean
import kotlinx.coroutines.*
import java.io.Closeable
import kotlin.coroutines.CoroutineContext

fun <T> ResultBean<T>.toData(): T? =
    if (isOk) data else throw FirmException(message = msg, code = code, data = data)

/**
 * 业务异常类
 */
class FirmException(override var message: String? = "", val code: Int, val data: Any?) :
    RuntimeException(message)

/**
 * 自定义网络常类
 */
class NotNetworkException : RuntimeException("")

/**
 * 使用类的方式调用方便以后实现重试机制
 */
class ApiRequest(var scope: CoroutineScope) {
    companion object {
        private const val RETRY_DELAY_MILLIS = 2000
        private const val MAX_RETRIES = 3
    }

    private val maxRetries: Int = MAX_RETRIES
    private val retryDelayMillis: Int = RETRY_DELAY_MILLIS
    fun <T> launchUI(
        block: suspend CoroutineScope.() -> T,
        error: ((Throwable) -> Unit)? = null,
        complete: (() -> Unit)? = null
    ): Job = launchUI(block, error, complete, null)

    fun <T> launchUI(
        block: suspend CoroutineScope.() -> T,
        error: ((Throwable) -> Unit)? = null,
        complete: (() -> Unit)? = null,
        apiObserver: ApiObserver<T>? = null
    ): Job {
        apiObserver?.onChanged(ApiProcess())
        return scope.launch(CoroutineExceptionHandler { _, throwable ->
            error?.invoke(throwable)
            apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.ERROR, throwable = throwable))
        }) {
            try {
                val result = block()
                apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.SUCCESS, data = result))
            } catch (e: Throwable) {
                // 后续扩展实现重试机制
                try {
                    error?.invoke(e)
                    apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.ERROR, throwable = e))
                } catch (e1: Exception) {
                    apiObserver?.onChanged(ApiProcess(apiCode = ApiCode.ERROR, throwable = e1))
                }
            }
            try {
                complete?.invoke()
                apiObserver?.onChanged(ApiProcess(ApiCode.COMPLETE))
            } catch (e1: Exception) {
                apiObserver?.onChanged(ApiProcess(ApiCode.COMPLETE))
            }
        }
    }
}

/**
 * 在非viewmodel地方使用
 */
fun uiScope() = SafeCoroutineScope(Dispatchers.Main)

class SafeCoroutineScope(context: CoroutineContext) : CoroutineScope, Closeable {
    override val coroutineContext: CoroutineContext = SupervisorJob() + context

    override fun close() {
        coroutineContext.cancelChildren()
    }
}